# import numpy as np：导入了 Python 的科学计算库 numpy，并使用别名 np。numpy 在处理数值计算、数组操作等方面非常强大，
# 代码中主要用于将评分数据转换为数组形式，方便后续进行相似度计算等操作。
import numpy as np
# from sklearn.metrics.pairwise import cosine_similarity：从 sklearn（Scikit-learn，一个常用的机器学习库）的
# metrics.pairwise 模块中导入了 cosine_similarity 函数，用于计算两个向量之间的余弦相似度，在这里就是计算不同用户评分向量之间的相似度，
# 以衡量用户之间的相似程度。
from sklearn.metrics.pairwise import cosine_similarity
# import os 和 import json：分别导入了 Python 标准库中的 os 模块（用于操作系统相关的操作，如设置环境变量等）和 json 模块（用于处理 JSON 格式的数据）。
import os
import json
# import django：导入了 django 框架，后续通过设置环境变量并调用 django.setup() 来初始化 Django 环境，以便能与 Django 项目中的数据库和模型进行交互。
import django
os.environ.setdefault('DJANGO_SETTINGS_MODULE','travel.settings')
django.setup()
# from app.models import TravelInfo：从 Django 项目的 app 应用下的 models 模块中导入了 TravelInfo 模型类，
# 用于获取数据库中存储的旅游信息数据，从中提取用户的评分信息等。
from app.models import TravelInfo

user_ratings = {
    "admins": {"广州塔": 5},
    "user2": {"广州塔": 5, "三亚蜈支洲岛旅游区": 2},

}


# 函数目的：从数据库中存储的旅游信息（TravelInfo 模型对应的数据）里的用户评论数据中提取出用户对各个景点的评分信息，整理成以用户 ID 为键，
# 每个用户对应的景点评分字典为值的嵌套字典形式并返回。
def getUser_ratings():
    # 实现逻辑：
    # 首先初始化一个空字典 user_ratings，用于存储最终整理好的用户评分数据。
    user_ratings = {}
    # 然后通过循环遍历 TravelInfo.objects.all() 获取的所有旅游信息对象。
    for travel in TravelInfo.objects.all():
        # 对于每个旅游信息对象，先使用 json.loads(travel.comments) 将存储为 JSON 字符串形式的评论数据解析为 Python 列表
        # （评论数据是一个包含多个评论字典的列表，每个字典包含 userId（用户 ID）、score（评分）等相关信息）。
        comments = json.loads(travel.comments)
        # 接着内层循环遍历解析后的评论列表中的每个评论字典 com，
        for com in comments:
            # 通过 try-except 语句尝试获取 com['userId']，
            try:
                #
                com['userId']
            except:
                continue
            # 如果不存在该键（可能评论数据格式不完全符合预期）则跳过当前评论，继续下一个。如果用户 ID 在 user_ratings 字典中不存在
            # （通过 user_ratings.get(com['userId'],-1) == -1 判断），则以该用户 ID 为键，创建一个新的字典，
            # 其中包含当前旅游景点（travel.title）及其对应的评分（com['score']）作为值；
            if user_ratings.get(com['userId'],-1) == -1:
                user_ratings[com['userId']] = {travel.title:com['score']}
            else:
                # 如果用户 ID 已存在，则直接将当前景点及其评分添加到对应用户的评分字典中。
                user_ratings[com['userId']][travel.title] = com['score']
    # 最后返回整理好的包含所有用户评分信息的 user_ratings 字典。
    return user_ratings

def user_bases_collaborative_filtering(user_id,user_ratings,top_n=20):
# def user_bases_collaborative_filtering(user_id, user_ratings, top_n=3):
    # 获取目标用户的评分数据
    target_user_ratings = user_ratings[user_id]

    # 初始化一个字段，用于保存其他用户与目标用户的相似度得分
    user_similarity_scores = {}

    # 将目标用户的评分转化为numpy数组
    target_user_ratings_list = np.array([
        rating for _ , rating in target_user_ratings.items()
    ])

    # 计算目标用户与其他用户之间的相似度得分
    for user,ratings in user_ratings.items():
        if user == user_id:
            continue
        # 将其他用户的评分转化为numpy数组
        user_ratings_list = np.array([ratings.get(item,0) for item in target_user_ratings])
        # 计算余弦相似度
        similarity_score = cosine_similarity([user_ratings_list],[target_user_ratings_list])[0][0]
        user_similarity_scores[user] = similarity_score

    # 对用户相似度得分进行降序排序
    sorted_similar_user = sorted(user_similarity_scores.items(),key=lambda x:x[1],reverse=True)

    # 选择 TOP N 个相似用户喜欢的景点 作为推荐结果
    recommended_items = set()
    for similar_user,_ in sorted_similar_user[:top_n]:
        recommended_items.update(user_ratings[similar_user].keys())

    # 过滤掉目标用户已经评分过的景点
    recommended_items = [item for item in recommended_items if item not in target_user_ratings]

    return recommended_items


if __name__ == '__main__':
    # 首先定义 user_id 为 1，表示要为 ID 为 1 的用户进行景点推荐。
    user_id =1
    # 然后调用 getUser_ratings 函数获取所有用户的评分数据。
    user_ratings = getUser_ratings()
    # 最后调用 user_bases_collaborative_filtering 函数，传入目标用户 ID 和所有用户评分数据，获取并得到推荐给该用户的旅游景点列表。
    recommended_items = user_bases_collaborative_filtering(user_id,user_ratings)
